1   /*
2    * Copyright (C) 2009 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect;
18  
19  import static com.google.common.base.Preconditions.checkArgument;
20  import static com.google.common.base.Preconditions.checkNotNull;
21  
22  import java.util.ArrayList;
23  import java.util.Collection;
24  import java.util.Collections;
25  import java.util.Comparator;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.SortedSet;
29  import java.util.TreeSet;
30  
31  import javax.annotation.Nullable;
32  
33  /**
34   * GWT emulation of {@link ImmutableSortedSet}.
35   *
36   * @author Hayward Chan
37   */
38  public abstract class ImmutableSortedSet<E>
39      extends ForwardingImmutableSet<E> implements SortedSet<E>, SortedIterable<E> {
40    // TODO(cpovirk): split into ImmutableSortedSet/ForwardingImmutableSortedSet?
41  
42    // In the non-emulated source, this is in ImmutableSortedSetFauxverideShim,
43    // which overrides ImmutableSet & which ImmutableSortedSet extends.
44    // It is necessary here because otherwise the builder() method
45    // would be inherited from the emulated ImmutableSet.
46    // TODO(cpovirk): should we be including other methods from the shim here and
47    // in ImmutableSortedMap?
48    @Deprecated public static <E> ImmutableSortedSet.Builder<E> builder() {
49      throw new UnsupportedOperationException();
50    }
51  
52    // TODO: Can we find a way to remove this @SuppressWarnings even for eclipse?
53    @SuppressWarnings("unchecked")
54    private static final Comparator NATURAL_ORDER = Ordering.natural();
55  
56    @SuppressWarnings("unchecked")
57    private static final ImmutableSortedSet<Object> NATURAL_EMPTY_SET =
58        new EmptyImmutableSortedSet<Object>(NATURAL_ORDER);
59  
60    @SuppressWarnings("unchecked")
61    private static <E> ImmutableSortedSet<E> emptySet() {
62      return (ImmutableSortedSet<E>) NATURAL_EMPTY_SET;
63    }
64  
65    static <E> ImmutableSortedSet<E> emptySet(
66        Comparator<? super E> comparator) {
67      checkNotNull(comparator);
68      if (NATURAL_ORDER.equals(comparator)) {
69        return emptySet();
70      } else {
71        return new EmptyImmutableSortedSet<E>(comparator);
72      }
73    }
74  
75    public static <E> ImmutableSortedSet<E> of() {
76      return emptySet();
77    }
78  
79    public static <E extends Comparable<? super E>> ImmutableSortedSet<E> of(
80        E element) {
81      return ofInternal(Ordering.natural(), element);
82    }
83  
84    @SuppressWarnings("unchecked")
85    public static <E extends Comparable<? super E>> ImmutableSortedSet<E> of(
86        E e1, E e2) {
87      return ofInternal(Ordering.natural(), e1, e2);
88    }
89  
90    @SuppressWarnings("unchecked")
91    public static <E extends Comparable<? super E>> ImmutableSortedSet<E> of(
92        E e1, E e2, E e3) {
93      return ofInternal(Ordering.natural(), e1, e2, e3);
94    }
95  
96    @SuppressWarnings("unchecked")
97    public static <E extends Comparable<? super E>> ImmutableSortedSet<E> of(
98        E e1, E e2, E e3, E e4) {
99      return ofInternal(Ordering.natural(), e1, e2, e3, e4);
100   }
101 
102   @SuppressWarnings("unchecked")
103   public static <E extends Comparable<? super E>> ImmutableSortedSet<E> of(
104       E e1, E e2, E e3, E e4, E e5) {
105     return ofInternal(Ordering.natural(), e1, e2, e3, e4, e5);
106   }
107 
108   @SuppressWarnings("unchecked")
109   public static <E extends Comparable<? super E>> ImmutableSortedSet<E> of(
110       E e1, E e2, E e3, E e4, E e5, E e6, E... remaining) {
111     int size = remaining.length + 6;
112     List<E> all = new ArrayList<E>(size);
113     Collections.addAll(all, e1, e2, e3, e4, e5, e6);
114     Collections.addAll(all, remaining);
115     // This is messed up. See TODO at top of file.
116     return ofInternal(Ordering.natural(), (E[]) all.toArray(new Comparable[0]));
117   }
118 
119   private static <E> ImmutableSortedSet<E> ofInternal(
120       Comparator<? super E> comparator, E... elements) {
121     checkNotNull(elements);
122     switch (elements.length) {
123       case 0:
124         return emptySet(comparator);
125       default:
126         SortedSet<E> delegate = new TreeSet<E>(comparator);
127         for (E element : elements) {
128           checkNotNull(element);
129           delegate.add(element);
130         }
131         return new RegularImmutableSortedSet<E>(delegate, false);
132     }
133   }
134 
135   public static <E> ImmutableSortedSet<E> copyOf(Collection<? extends E> elements) {
136     return copyOfInternal((Ordering<E>) Ordering.natural(), (Collection) elements, false);
137   }
138 
139   public static <E> ImmutableSortedSet<E> copyOf(Iterable<? extends E> elements) {
140     return copyOfInternal((Ordering<E>) Ordering.natural(), (Iterable) elements, false);
141   }
142 
143   public static <E> ImmutableSortedSet<E> copyOf(Iterator<? extends E> elements) {
144     return copyOfInternal((Ordering<E>) Ordering.natural(), (Iterator) elements);
145   }
146 
147   public static <E extends Comparable<? super E>> ImmutableSortedSet<E> copyOf(
148       E[] elements) {
149     return ofInternal(Ordering.natural(), elements);
150   }
151 
152   public static <E> ImmutableSortedSet<E> copyOf(
153       Comparator<? super E> comparator, Iterable<? extends E> elements) {
154     checkNotNull(comparator);
155     return copyOfInternal(comparator, elements, false);
156   }
157 
158   public static <E> ImmutableSortedSet<E> copyOf(
159       Comparator<? super E> comparator, Collection<? extends E> elements) {
160     checkNotNull(comparator);
161     return copyOfInternal(comparator, elements, false);
162   }
163 
164   public static <E> ImmutableSortedSet<E> copyOf(
165       Comparator<? super E> comparator, Iterator<? extends E> elements) {
166     checkNotNull(comparator);
167     return copyOfInternal(comparator, elements);
168   }
169 
170   @SuppressWarnings("unchecked")
171   public static <E> ImmutableSortedSet<E> copyOfSorted(SortedSet<E> sortedSet) {
172     Comparator<? super E> comparator = sortedSet.comparator();
173     if (comparator == null) {
174       comparator = NATURAL_ORDER;
175     }
176     return copyOfInternal(comparator, sortedSet.iterator());
177   }
178 
179   private static <E> ImmutableSortedSet<E> copyOfInternal(
180       Comparator<? super E> comparator, Iterable<? extends E> elements,
181       boolean fromSortedSet) {
182     checkNotNull(comparator);
183 
184     boolean hasSameComparator
185         = fromSortedSet || hasSameComparator(elements, comparator);
186     if (hasSameComparator && (elements instanceof ImmutableSortedSet)) {
187       @SuppressWarnings("unchecked")
188       ImmutableSortedSet<E> result = (ImmutableSortedSet<E>) elements;
189       boolean isSubset = (result instanceof RegularImmutableSortedSet)
190           && ((RegularImmutableSortedSet) result).isSubset;
191       if (!isSubset) {
192         // Only return the original copy if this immutable sorted set isn't
193         // a subset of another, to avoid memory leak.
194         return result;
195       }
196     }
197     return copyOfInternal(comparator, elements.iterator());
198   }
199 
200   private static <E> ImmutableSortedSet<E> copyOfInternal(
201       Comparator<? super E> comparator, Iterator<? extends E> elements) {
202     checkNotNull(comparator);
203     if (!elements.hasNext()) {
204       return emptySet(comparator);
205     }
206     SortedSet<E> delegate = new TreeSet<E>(comparator);
207     while (elements.hasNext()) {
208       E element = elements.next();
209       checkNotNull(element);
210       delegate.add(element);
211     }
212     return new RegularImmutableSortedSet<E>(delegate, false);
213   }
214 
215   private static boolean hasSameComparator(
216       Iterable<?> elements, Comparator<?> comparator) {
217     if (elements instanceof SortedSet) {
218       SortedSet<?> sortedSet = (SortedSet<?>) elements;
219       Comparator<?> comparator2 = sortedSet.comparator();
220       return (comparator2 == null)
221           ? comparator == Ordering.natural()
222           : comparator.equals(comparator2);
223     }
224     return false;
225   }
226 
227   // Assumes that delegate doesn't have null elements and comparator.
228   static <E> ImmutableSortedSet<E> unsafeDelegateSortedSet(
229       SortedSet<E> delegate, boolean isSubset) {
230     return delegate.isEmpty()
231         ? emptySet(delegate.comparator())
232         : new RegularImmutableSortedSet<E>(delegate, isSubset);
233   }
234 
235   // This reference is only used by GWT compiler to infer the elements of the
236   // set that needs to be serialized.
237   private Comparator<E> unusedComparatorForSerialization;
238   private E unusedElementForSerialization;
239 
240   private transient final SortedSet<E> sortedDelegate;
241 
242   /**
243    * Scary constructor for ContiguousSet. This constructor (in this file, the
244    * GWT emulation of ImmutableSortedSet) creates an empty sortedDelegate,
245    * which, in a vacuum, sets this object's contents to empty.  By contrast,
246    * the non-GWT constructor with the same signature uses the comparator only
247    * as a comparator. It does NOT assume empty contents. (It requires an
248    * implementation of iterator() to define its contents, and methods like
249    * contains() are implemented in terms of that method (though they will
250    * likely be overridden by subclasses for performance reasons).) This means
251    * that a call to this method have can different behavior in GWT and non-GWT
252    * environments UNLESS subclasses are careful to always override all methods
253    * implemented in terms of sortedDelegate (except comparator()).
254    */
255   ImmutableSortedSet(Comparator<? super E> comparator) {
256     this(Sets.newTreeSet(comparator));
257   }
258 
259   ImmutableSortedSet(SortedSet<E> sortedDelegate) {
260     super(sortedDelegate);
261     this.sortedDelegate = Collections.unmodifiableSortedSet(sortedDelegate);
262   }
263 
264   public Comparator<? super E> comparator() {
265     return sortedDelegate.comparator();
266   }
267 
268   @Override // needed to unify SortedIterable and Collection iterator() methods
269   public UnmodifiableIterator<E> iterator() {
270     return super.iterator();
271   }
272 
273   @Override
274   public Object[] toArray() {
275     return ObjectArrays.toArrayImpl(this);
276   }
277 
278   @Override
279   public <T> T[] toArray(T[] other) {
280     return ObjectArrays.toArrayImpl(this, other);
281   }
282 
283   @Override public boolean contains(@Nullable Object object) {
284     try {
285       // This set never contains null.  We need to explicitly check here
286       // because some comparator might throw NPE (e.g. the natural ordering).
287       return object != null && sortedDelegate.contains(object);
288     } catch (ClassCastException e) {
289       return false;
290     }
291   }
292 
293   @Override public boolean containsAll(Collection<?> targets) {
294     for (Object target : targets) {
295       if (target == null) {
296         // This set never contains null.  We need to explicitly check here
297         // because some comparator might throw NPE (e.g. the natural ordering).
298         return false;
299       }
300     }
301     try {
302       return sortedDelegate.containsAll(targets);
303     } catch (ClassCastException e) {
304       return false;
305     }
306   }
307 
308   public E first() {
309     return sortedDelegate.first();
310   }
311 
312   public ImmutableSortedSet<E> headSet(E toElement) {
313     checkNotNull(toElement);
314     try {
315       return unsafeDelegateSortedSet(sortedDelegate.headSet(toElement), true);
316     } catch (IllegalArgumentException e) {
317       return emptySet(comparator());
318     }
319   }
320 
321   E higher(E e) {
322     checkNotNull(e);
323     Iterator<E> iterator = tailSet(e).iterator();
324     while (iterator.hasNext()) {
325       E higher = iterator.next();
326       if (comparator().compare(e, higher) < 0) {
327         return higher;
328       }
329     }
330     return null;
331   }
332 
333   ImmutableSortedSet<E> headSet(E toElement, boolean inclusive) {
334     checkNotNull(toElement);
335     if (inclusive) {
336       E tmp = higher(toElement);
337       if (tmp == null) {
338         return this;
339       }
340       toElement = tmp;
341     }
342     return headSet(toElement);
343   }
344 
345   public E last() {
346     return sortedDelegate.last();
347   }
348 
349   public ImmutableSortedSet<E> subSet(E fromElement, E toElement) {
350     return subSet(fromElement, true, toElement, false);
351   }
352 
353   ImmutableSortedSet<E> subSet(E fromElement, boolean fromInclusive, E toElement,
354       boolean toInclusive) {
355     checkNotNull(fromElement);
356     checkNotNull(toElement);
357     int cmp = comparator().compare(fromElement, toElement);
358     checkArgument(cmp <= 0, "fromElement (%s) is less than toElement (%s)", fromElement, toElement);
359     if (cmp == 0 && !(fromInclusive && toInclusive)) {
360       return emptySet(comparator());
361     }
362     return tailSet(fromElement, fromInclusive).headSet(toElement, toInclusive);
363   }
364 
365   public ImmutableSortedSet<E> tailSet(E fromElement) {
366     checkNotNull(fromElement);
367     try {
368       return unsafeDelegateSortedSet(sortedDelegate.tailSet(fromElement), true);
369     } catch (IllegalArgumentException e) {
370       return emptySet(comparator());
371     }
372   }
373 
374   ImmutableSortedSet<E> tailSet(E fromElement, boolean inclusive) {
375     checkNotNull(fromElement);
376     if (!inclusive) {
377       E tmp = higher(fromElement);
378       if (tmp == null) {
379         return emptySet(comparator());
380       }
381       fromElement = tmp;
382     }
383     return tailSet(fromElement);
384   }
385 
386   public static <E> Builder<E> orderedBy(Comparator<E> comparator) {
387     return new Builder<E>(comparator);
388   }
389 
390   public static <E extends Comparable<?>> Builder<E> reverseOrder() {
391     return new Builder<E>(Ordering.natural().reverse());
392   }
393 
394   public static <E extends Comparable<?>> Builder<E> naturalOrder() {
395     return new Builder<E>(Ordering.natural());
396   }
397 
398   public static final class Builder<E> extends ImmutableSet.Builder<E> {
399     private final Comparator<? super E> comparator;
400 
401     public Builder(Comparator<? super E> comparator) {
402       this.comparator = checkNotNull(comparator);
403     }
404 
405     @Override public Builder<E> add(E element) {
406       super.add(element);
407       return this;
408     }
409 
410     @Override public Builder<E> add(E... elements) {
411       super.add(elements);
412       return this;
413     }
414 
415     @Override public Builder<E> addAll(Iterable<? extends E> elements) {
416       super.addAll(elements);
417       return this;
418     }
419 
420     @Override public Builder<E> addAll(Iterator<? extends E> elements) {
421       super.addAll(elements);
422       return this;
423     }
424 
425     @Override public ImmutableSortedSet<E> build() {
426       return copyOfInternal(comparator, contents.iterator());
427     }
428   }
429 }